package cn.mini.mybatis.mybatis.parsing;


/**
 * 通用记号解析器，用于处理#{}和${}参数占位符
 *
 * @description 普通记号解析器，处理#{}和${}参数
 */
public class GenericTokenParser {
    // 开始标记，如 "#{" 或 "${"
    private final String openToken;
    // 结束标记，通常是 "}"
    private final String closeToken;
    // 记号处理器，用于处理解析出来的内容
    private final TokenHandler handler;

    /**
     * 构造函数
     *
     * @param openToken  开始标记
     * @param closeToken 结束标记
     * @param handler    记号处理器
     */
    public GenericTokenParser(String openToken, String closeToken, TokenHandler handler) {
        this.openToken = openToken;
        this.closeToken = closeToken;
        this.handler = handler;
    }

    /**
     * 解析文本中的标记
     *parse方法的执行流程
     * 初始化准备
     * 创建StringBuilder用于构建结果，将输入文本转为字符数组便于操作，定义offset记录当前处理位置。
     * 循环查找标记
     * 从offset开始，查找第一个openToken的位置（start）。
     * 若未找到openToken，直接将剩余文本添加到结果，结束解析。
     * 处理转义场景
     * 若openToken前有反斜杠\（如\${name}），说明标记被转义：
     * 移除反斜杠，保留openToken本身（不解析为可替换标记）。
     * 更新offset到openToken之后，继续查找下一个标记。
     * 处理正常标记
     * 找到有效openToken后，查找对应的closeToken：
     * 若未找到closeToken，将剩余文本直接加入结果。
     * 若找到closeToken：
     * 先添加openToken之前的文本到结果。
     * 提取openToken和closeToken之间的内容（如#{id}中的id）。
     * 调用TokenHandler.handleToken处理内容，并将结果添加到StringBuilder。
     * 更新offset到closeToken之后，继续循环查找下一个标记。
     * 收尾处理
     * 将未处理的剩余文本添加到结果，返回最终字符串。
     * @param text 待解析的文本
     * @return 解析后的文本
     */
    public String parse(String text) {
        // 使用StringBuilder构建结果字符串
        StringBuilder builder = new StringBuilder();

        // 检查输入文本是否有效
        if (text != null && text.length() > 0) {
            // 将文本转换为字符数组以便处理
            char[] src = text.toCharArray();
            // 偏移量，用于记录当前处理的位置
            int offset = 0;
            // 查找第一个开始标记的位置
            int start = text.indexOf(openToken, offset);

            // 循环解析所有参数标记，如可以解析${first_name} ${initial} ${last_name} reporting这样的字符串
            while (start > -1) {
                // 判断开始标记前是否是反斜杠转义符（用于处理转义的标记）
                if (start > 0 && src[start - 1] == '\\') {
                    // 变量被转义，需要移除反斜杠
                    // 将转义前的内容添加到结果中
                    builder.append(src, offset, start - offset - 1).append(openToken);
                    // 更新偏移量到开始标记之后
                    offset = start + openToken.length();
                } else {
                    // 找到对应的结束标记位置
                    int end = text.indexOf(closeToken, start);
                    if (end == -1) {
                        // 没有找到结束标记，将剩余内容全部添加到结果中
                        builder.append(src, offset, src.length - offset);
                        offset = src.length;
                    } else {
                        // 找到了结束标记
                        // 将开始标记前的内容添加到结果中
                        builder.append(src, offset, start - offset);
                        // 更新偏移量到开始标记之后
                        offset = start + openToken.length();
                        // 提取标记中的内容（如#{favouriteSection,jdbcType=VARCHAR}中的favouriteSection,jdbcType=VARCHAR）
                        String content = new String(src, offset, end - offset);
                        // 使用处理器处理提取的内容，并将结果添加到结果中
                        builder.append(handler.handleToken(content));
                        // 更新偏移量到结束标记之后
                        offset = end + closeToken.length();
                    }
                }
                // 查找下一个开始标记的位置
                start = text.indexOf(openToken, offset);
            }

            // 将剩余未处理的内容添加到结果中
            if (offset < src.length) {
                builder.append(src, offset, src.length - offset);
            }
        }

        // 返回解析后的结果
        return builder.toString();
    }

}
